package controller.net;

import controller.exceptions.InvalidVersionException;
import controller.exceptions.ItemAlreadyRemovedException;
import filesystem.exceptions.InvalidModelItemException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.zip.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import model.Album;
import model.Model;
import model.ModelItem;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

/**
 *
 * Client class
 */
public class NetControllerClient {

    private Socket clientSocket;
    private ObjectOutputStream out = null;
    private ObjectInputStream in = null;

    public enum operation {

        ADD,
        EDIT,
        REMOVE
    }

    /**
     * Types of the model
     */
    public enum type {

        ALBUM,
        ARTIST,
        GENRE,
        TRACK
    }

    public NetControllerClient() {
        setConnectionParametrs();
    }

    public Socket getClientSocket() {
        return clientSocket;
    }

    private void setConnectionParametrs() {
        Document config = null;
        try {
            config = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse("src/controller/net/config.xml");
        } catch (ParserConfigurationException e) {
            //TODO
        } catch (SAXException e) {
            //TODO
        } catch (IOException e) {
            //TODO
        }
        String host = config.getDocumentElement().getAttribute("host");
        String port = config.getDocumentElement().getAttribute("port");
        try {
            clientSocket = new Socket(host, Integer.parseInt(port));
            out = new ObjectOutputStream(clientSocket.getOutputStream());
            in = new ObjectInputStream(clientSocket.getInputStream());
        } catch (IOException e) {
            //TODO
            int i = 0;
        }
    }

    /**
     * Execute transaction on the server model
     *
     * @param o type of transaction (ADD, EDIT, REMOVE)
     * @param model model
     * @param item changed object of the model
     */
    public void execute(operation o, type t, ModelItem item)
            throws InvalidModelItemException, IOException, 
            InvalidVersionException, ItemAlreadyRemovedException {
        setConnectionParametrs();
        switch (o) {
            case ADD:
                out.writeObject("ADD");
                break;
            case EDIT:
                out.writeObject("EDIT");
                break;
            case REMOVE:
                out.writeObject("REMOVE");
                break;
        }
        String modelType = getTypeString(t);
        out.writeObject(modelType);
        out.writeObject(item);
        try {
            String answer = (String) in.readObject();
            switch (answer) {
                case "OK":
                    return;
                case "OLD_VERSION":
                    throw new InvalidVersionException();
                case "ALREADY_REMOVED":
                    throw new ItemAlreadyRemovedException();
            }
        } catch (ClassNotFoundException e) {
        }
        out.writeObject("END");
    }

    /**
     * Select objects from the server model by parametr
     *
     * @param model model
     * @param parametr parametr of the search
     * @return List of the found objects
     */
    public ArrayList selectItems(type t, ModelItem parametr) throws IOException {
        setConnectionParametrs();
        //отправляем тип команды
        out.writeObject("SELECT");
        //отправляем тип модели
        String modelType = getTypeString(t);
        out.writeObject(modelType);
        out.writeObject(parametr);
        ArrayList items = null;
        try {
            //получаем количество найденных объектов
            int count = (int) in.readObject();
            items = new ArrayList();
            for (int i = 0; i < count; i++) {
                ModelItem item = (ModelItem) in.readObject();
                items.add(item);
            }
        } catch (ClassNotFoundException e) {
            //TODO
            int o = 0;
        } catch (IOException e) {
            //TODO
            int o = 0;
        }
        //ждем подтверждения успешной операции
        try {
            if (!((String) in.readObject()).equals("OK")) {
                //всё плохо
                //TODO
            }
        } catch (ClassNotFoundException e) {
        }
        //прощаемся с сервером
        out.writeObject("END");
        return items;
    }

    private String getTypeString(type t) {
        switch (t) {
            case ALBUM:
                return "ALBUM";
            case ARTIST:
                return "ARTIST";
            case GENRE:
                return "GENRE";
            case TRACK:
                return "TRACK";
        }
        return null;
    }
}
